/*
 * Created on Dec 24, 2003
 */
package com.unash.z3950.pdu;

/**
 * @author Ashwini Gokhale
 */
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import com.unash.asn.AsnInteger;
import com.unash.asn.AsnObjectIdentifier;
import com.unash.asn.AsnTags;
import com.unash.asn.AsnVisibleString;
import com.unash.asn.BerDecoder;
import com.unash.util.AsnUtil;
import com.unash.util.Consts;


public class Z3950BerDecoder implements Z3950Tags, Z3950Decoder, Consts , AsnTags{
	
	public Request decodeRequest(InputStream iStream) throws Exception{
		Request objRequest = null;
		AsnUtil.TagInfo tagInfo = AsnUtil.decodeTag(iStream);
		int length = AsnUtil.decodeLength(iStream);
		byte[] request = AsnUtil.readNBytes(iStream,length);
		ByteArrayInputStream bAStream = new ByteArrayInputStream(request);
		switch(tagInfo.tag){
			case INIT_REQUEST:
				objRequest = decodeInitRequest(bAStream);
				break;
			case SEARCH_REQUEST:
				objRequest = decodeSearchRequest(bAStream);
				break;
			case PRESENT_REQUEST:
				objRequest = decodePresentRequest(bAStream);
			default:
				System.out.println("Request other than Init.");
				break;
		}//switch
		return objRequest;
	}//decodeRequest
	
	
	public InitRequest decodeInitRequest(ByteArrayInputStream bAStream)
	throws Exception{
		InitRequest result = new InitRequest();
		while(ZERO < bAStream.available()){
			AsnUtil.TagInfo tagInfo = AsnUtil.decodeTag(bAStream);
			int length = AsnUtil.decodeLength(bAStream);
			switch(tagInfo.tag){
				case REFERENCE_ID:
					result.setReferenceId(BerDecoder.decodeOctetString(bAStream,length));
					break;
				case PROTOCOL_VERSION:
					result.setProtocolVersion(BerDecoder.decodeBitString(bAStream,length));
					break;
				case OPTIONS:
					result.setOptions(BerDecoder.decodeBitString(bAStream,length));
					break;
				case PREFERRED_MESSAGE_SIZE:
					result.setPreferredMessageSize(BerDecoder.decodeInteger(bAStream,length)); 
					break;
				case EXCEPTIONAL_RECORD_SIZE:
					result.setExceptionalRecordSize(BerDecoder.decodeInteger(bAStream,length));
					break;
				case ID_AUTHENTICATION:
					//TODO: IDAuthentication--ANY
					break;
				case IMPLEMENTATION_ID:
					result.setImplementationId(BerDecoder.decodeVisibleString(bAStream,length));
					break;
				case IMPLEMENTATION_NAME:
					result.setImplementationName(BerDecoder.decodeVisibleString(bAStream,length));
					break;
				case IMPLEMENTATION_VERSION:
					result.setImplementationVersion(BerDecoder.decodeVisibleString(bAStream,length));
					break;
				case USER_INFORMATION:
					//TODO: userInformationField [EXTERNAL Optional]
					break;
				case OTHER_INFORMATION:
					//TODO: otherInformation [OtherInformation Optional]
					break;
				default:			
				
			}//switch
		}//while
				
		return result;
	}//decodeInitRequest
	
	
	public SearchRequest decodeSearchRequest(ByteArrayInputStream bAStream)
	throws Exception{
		SearchRequest result = new SearchRequest();
		while(ZERO < bAStream.available()){
			AsnUtil.TagInfo tagInfo = AsnUtil.decodeTag(bAStream);
			int length = AsnUtil.decodeLength(bAStream);
			switch(tagInfo.tag){
				case REFERENCE_ID:
					result.setReferenceId(BerDecoder.decodeOctetString(bAStream,length));
					break;
				case SMALL_SET_UPPER_BOUND:
					result.setSmallSetUpperBound(BerDecoder.decodeInteger(bAStream,length));
					break;
				case LARGE_SET_LOWER_BOUND:
					result.setLargeSetLowerBound(BerDecoder.decodeInteger(bAStream,length));
					break;
				case MEDIUM_SET_PRESENT_NUMBER:
					result.setMediumSetPresentNumber(BerDecoder.decodeInteger(bAStream,length));
					break;
				case REPLACE_INDICATOR:
					result.setReplaceIndicator(BerDecoder.decodeBoolean(bAStream));
					break;
				case RESULT_SET_NAME:
					result.setResultSetName(BerDecoder.decodeVisibleString(bAStream,length));
					break;
				case DATABASE_NAMES:
					result.setDatabaseNames(decodeSequenceOfDatabase(bAStream,length));
					break;
				case SMALL_SET_ELEMENT_SET_NAMES:
					result.setSmallSetElementSetNames(decodeChoiceElementSetNames(bAStream));
					break;
				case MEDIUM_SET_ELEMENT_SET_NAMES:
					result.setMediumSetElementSetNames(decodeChoiceElementSetNames(bAStream));
					break;
				case PREFERRED_RECORD_SYNTAX:
					result.setPreferredRecordSyntax(BerDecoder.decodeObjectIdentifier(bAStream,length));
					break;
				case QUERY:
					result.setQuery(decodeChoiceQuery(bAStream));
					break;
				default:
					System.out.println("decodeSearchRequest");
					break;
			}//switch
		}//while
		
		return result;
	}//decodeSearchRequest
	
	
	public PresentRequest decodePresentRequest(ByteArrayInputStream bAStream)
	throws Exception{
		PresentRequest result = new PresentRequest();
		while(ZERO < bAStream.available()){
			AsnUtil.TagInfo tagInfo = AsnUtil.decodeTag(bAStream);
			int length = AsnUtil.decodeLength(bAStream);
			switch(tagInfo.tag){
				case REFERENCE_ID:
					result.setReferenceId(BerDecoder.decodeOctetString(bAStream,length));
					break;
				case RESULT_SET_ID:
					result.setResultSetId(BerDecoder.decodeVisibleString(bAStream,length));
					break;
				case RESULT_SET_START_POINT:
					result.setResultSetStartPoint(BerDecoder.decodeInteger(bAStream,length));
					break;
				case NUMBER_OF_RECORDS_REQUESTED:
					result.setNumberOfRecordsRequested(BerDecoder.decodeInteger(bAStream,length));
					break;
				case ADDITIONAL_RANGES:
					result.setAdditionalRanges(decodeSequenceOfRange(bAStream,length));
					break;
				//case RECORD_COMPOSITION:
					case RECORD_COMPOSITION_SIMPLE:
						RecordCompositionSimple simple = new RecordCompositionSimple();
						simple.setSimple(decodeChoiceElementSetNames(bAStream));
						result.setRecordComposition(simple);	
						break;
					case RECORD_COMPOSITION_COMPLEX:
						RecordCompositionComplex complex = new RecordCompositionComplex();
						complex.setComplex(decodeSequenceCompSpec(bAStream,length));
						result.setRecordComposition(complex);
						break;	
				case PREFERRED_RECORD_SYNTAX:
					result.setPrefferedRecordSyntax(BerDecoder.decodeObjectIdentifier(bAStream,length));					
					break;
				case MAX_SEGMENT_COUNT:
					result.setMaxSegmentCount(BerDecoder.decodeInteger(bAStream,length));
					break;
				case MAX_RECORD_SIZE:
					result.setMaxRecordSize(BerDecoder.decodeInteger(bAStream,length));
					break;
				case MAX_SEGMENT_SIZE:
					result.setMaxSegmentSize(BerDecoder.decodeInteger(bAStream,length));
					break;
				//OtherInformation
			}//switch
			
		}//while
		
		return result;
	}//decodePresentRequest
	
	
	public Range[] decodeSequenceOfRange(ByteArrayInputStream bAStream, int len)
	throws Exception{
		List<Range> rangeList = new ArrayList<Range>();
		byte[] BA = AsnUtil.readNBytes(bAStream,len);
		ByteArrayInputStream bs = new ByteArrayInputStream(BA);
		while(ZERO < bs.available()){
			Range range = new Range();	
			AsnUtil.TagInfo tagInfo = AsnUtil.decodeTag(bs);
			int length = AsnUtil.decodeLength(bs);
			assert tagInfo.tag == SEQUENCE:"SequenceOfRange:Sequence";
			
			tagInfo = AsnUtil.decodeTag(bs);
			length = AsnUtil.decodeLength(bs);
			assert tagInfo.tag == SEQUENCE:"SequenceOfRange:StartingPosition";
			range.setStartingPosition(BerDecoder.decodeInteger(bs,length));
			
			tagInfo = AsnUtil.decodeTag(bs);
			length = AsnUtil.decodeLength(bs);
			assert tagInfo.tag == SEQUENCE:"SequenceOfRange:NumberOfRecords";
			range.setNumberOfRecords(BerDecoder.decodeInteger(bs,length));
			
			rangeList.add(range);
		}//while
		return (Range[])rangeList.toArray(new Range[0]);
	}//decodeSequenceOfRange
	
	
	public CompSpec decodeSequenceCompSpec(ByteArrayInputStream bAStream,int len)
	throws Exception{
		CompSpec compSpec = new CompSpec();
		while(ZERO < bAStream.available()){
			AsnUtil.TagInfo tagInfo = AsnUtil.decodeTag(bAStream);
			int length = AsnUtil.decodeLength(bAStream);
			switch(tagInfo.tag){
				case SELECT_ALTERNATIVE_SYNTAX:
					compSpec.setSelectAlternativeSyntax(BerDecoder.decodeBoolean(bAStream));
					break;
				case GENERIC:
					compSpec.setGeneric(decodeSequenceSpecification(bAStream,length));
					break;
				case DBSPECIFIC:
					compSpec.setDbSpecific(decodeSequenceOfCompSpecDatabaseSpecific(bAStream,length));
					break;
				case RECORD_SYNTAX:
					compSpec.setRecordSyntax(decodeSequenceOfAsnObjectIdentifier(bAStream,length));
					break;
			}//switch
		}//while
		return compSpec;
	}//decodeSequenceCompSpec
	
	
	public AsnObjectIdentifier[] decodeSequenceOfAsnObjectIdentifier(ByteArrayInputStream bAStream, int len)
	throws Exception{
		byte[] BA = AsnUtil.readNBytes(bAStream,len);
		ByteArrayInputStream bs = new ByteArrayInputStream(BA);
		List<AsnObjectIdentifier> objIdentifierList = new ArrayList<AsnObjectIdentifier>();
		while(ZERO < bs.available()){
			AsnUtil.TagInfo tagInfo = AsnUtil.decodeTag(bs);
			int length = AsnUtil.decodeLength(bs);
			objIdentifierList.add(BerDecoder.decodeObjectIdentifier(bs,length)); 	
		}//while
		return (AsnObjectIdentifier[])objIdentifierList.toArray(new AsnObjectIdentifier[0]);
	}//decodeSequenceOfObjectIdentifier
	
	
	public CompSpecDatabaseSpecific[] decodeSequenceOfCompSpecDatabaseSpecific(ByteArrayInputStream bAStream,int len)
	throws Exception{
		byte[] BA = AsnUtil.readNBytes(bAStream,len);
		ByteArrayInputStream bs = new ByteArrayInputStream(BA);
		List<CompSpecDatabaseSpecific> databaseSpecList = new ArrayList<CompSpecDatabaseSpecific>();
		while(ZERO < bs.available()){
			CompSpecDatabaseSpecific dbSpecific = new CompSpecDatabaseSpecific();
			
			AsnUtil.TagInfo tagInfo = AsnUtil.decodeTag(bs);
			int length = AsnUtil.decodeLength(bs);
			assert tagInfo.tag == SEQUENCE:"CompSpecDatabaseSpecific:Sequence";
			
			tagInfo = AsnUtil.decodeTag(bs);
			length = AsnUtil.decodeLength(bs);
			assert tagInfo.tag == COMP_SPEC_DB:"CompSpecDatabaseSpecific:DB";
			dbSpecific.setDatabaseName(BerDecoder.decodeVisibleString(bs,length));
			
			tagInfo = AsnUtil.decodeTag(bs);
			length = AsnUtil.decodeLength(bs);
			assert tagInfo.tag == COMP_SPEC_SPEC:"CompSpecDatabaseSpecific:Spec";
			dbSpecific.setSpec(decodeSequenceSpecification(bs,length));
			
			databaseSpecList.add(dbSpecific);
		}//while
		return (CompSpecDatabaseSpecific[])databaseSpecList.toArray(new CompSpecDatabaseSpecific[0]);
	}//decodeSequenceOfCompSpecDatabaseSpecific
	
	
	public Specification decodeSequenceSpecification(ByteArrayInputStream bAStream, int len)
	throws Exception{
		Specification specification = new Specification();
		while(ZERO < bAStream.available()){
			AsnUtil.TagInfo tagInfo = AsnUtil.decodeTag(bAStream);
			int length = AsnUtil.decodeLength(bAStream);
			switch(tagInfo.tag){
				case SCHEMA:
					specification.setSchema(BerDecoder.decodeObjectIdentifier(bAStream,length));
					break;
				case ELEMENT_SPEC:
					specification.setElementSpec(decodeChoiceElementSpec(bAStream));
					break;	
			}//switch
		}//while 
		return specification;
	}//decodeSequenceSpecification
	
	
	public ElementSpec decodeChoiceElementSpec(ByteArrayInputStream bAStream)
	throws Exception{
		ElementSpec elementSpec = null;
		AsnUtil.TagInfo tagInfo = AsnUtil.decodeTag(bAStream);
		int length = AsnUtil.decodeLength(bAStream);
		switch(tagInfo.tag){
			case SPEC_ELEMENT_SET_NAME:
				ElementSpecElementSetName elementSetName = new ElementSpecElementSetName();
				elementSetName.setElementSetName(BerDecoder.decodeVisibleString(bAStream,length));
				elementSpec = elementSetName;				 
				break;
			case SPEC_EXTERNAL_ESPEC:
				ElementSpecExternalEspec externalEspec = new ElementSpecExternalEspec();
				//externalEspec
				elementSpec = externalEspec;
				break;
		}//switch
		return elementSpec;
	}//decodeChoiceElementSpec
	
	
	public Query decodeChoiceQuery(ByteArrayInputStream bAStream)
	throws Exception{
		int length;
		AsnUtil.TagInfo tagInfo = AsnUtil.decodeTag(bAStream);
		Query query = null;
		switch(tagInfo.tag){
			case TYPE_0:
				//ANY
				tagInfo = AsnUtil.decodeTag(bAStream);
				length = AsnUtil.decodeLength(bAStream);
				AsnUtil.readNBytes(bAStream,length);
				break;
			case TYPE_1:
				length = AsnUtil.decodeLength(bAStream);
				query = decodeSequenceRPNQuery(bAStream,length);
				break;
			case TYPE_2:
				tagInfo = AsnUtil.decodeTag(bAStream);
				length = AsnUtil.decodeLength(bAStream);
				Type2 type2 = new Type2();
				type2.setType2(BerDecoder.decodeOctetString(bAStream,length));
				query = type2; 
				break;
			case TYPE_100:
				tagInfo = AsnUtil.decodeTag(bAStream);
				length = AsnUtil.decodeLength(bAStream);
				Type100 type100 = new Type100();
				type100.setType100(BerDecoder.decodeOctetString(bAStream,length));
				query = type100;
				break;
			case TYPE_101:
				length = AsnUtil.decodeLength(bAStream);
				query = decodeSequenceRPNQuery(bAStream,length);
				break;
			case TYPE_102:
				tagInfo = AsnUtil.decodeTag(bAStream);
				length = AsnUtil.decodeLength(bAStream);
				Type102 type102 = new Type102();
				type102.setType102(BerDecoder.decodeOctetString(bAStream,length));
				query = type102;
				break;
			default:
				System.out.println("decodeChoiceQuery");
				break;
		}//switch
		return query;
	}//decodeChoiceQuery
	
	
	public RPNQuery decodeSequenceRPNQuery(ByteArrayInputStream bAStream,int len)
	throws Exception{
		RPNQuery rpnQuery = new RPNQuery();
			
		AsnUtil.TagInfo tagInfo = AsnUtil.decodeTag(bAStream);
		int length = AsnUtil.decodeLength(bAStream);
		
		assert tagInfo.tag == OBJECT_IDENTIFIER:"decodeSequenceRPNQuery-objectIdentifier";
		
		rpnQuery.setAttributeSet(BerDecoder.decodeObjectIdentifier(bAStream,length));
		rpnQuery.setRpn(decodeChoiceRPNStructure(bAStream));
		
		return rpnQuery;
	}//decodeSequenceRPNQuery
	
	
	public RPNStructure decodeChoiceRPNStructure(ByteArrayInputStream bAStream)
	throws Exception{
		RPNStructure rpnStructure = null;
		AsnUtil.TagInfo tagInfo = AsnUtil.decodeTag(bAStream);
		int length = AsnUtil.decodeLength(bAStream);
		switch(tagInfo.tag){
			case OPERAND:
				rpnStructure = decodeChoiceOperand(bAStream);
				break;
			case RPN_RPN_OP:
				RpnRpnOp rpnRpnOp = new RpnRpnOp();
				rpnRpnOp.setRpn1(decodeChoiceRPNStructure(bAStream));
				rpnRpnOp.setRpn2(decodeChoiceRPNStructure(bAStream));
				tagInfo = AsnUtil.decodeTag(bAStream);
				assert tagInfo.tag ==OPERATOR:"decodeChoiceRPNStructure:Operator";
				rpnRpnOp.setOp(decodeChoiceOperator(bAStream));
				rpnStructure = rpnRpnOp;
				
				break;
			default:
				System.out.println("decodeChoiceRPNStructure");
				break;
		}//switch
		
		return rpnStructure; 
	}//decodeRPNStructure
	
	
	public Operator decodeChoiceOperator(ByteArrayInputStream bAStream)
	throws Exception{
		int length = AsnUtil.decodeLength(bAStream);
		Operator operator = null;
		AsnUtil.TagInfo tagInfo = AsnUtil.decodeTag(bAStream);
		length = AsnUtil.decodeLength(bAStream);
		switch(tagInfo.tag){
			case OPERATOR_AND:
				OperatorAnd operatorAnd = new OperatorAnd();
				operator = operatorAnd;
				break;
			case OPERATOR_OR:
				OperatorOr operatorOr = new OperatorOr();
				operator = operatorOr;
				break;
			case OPERATOR_AND_NOT:
				OperatorAndNot operatorAndNot = new OperatorAndNot();
				operator = operatorAndNot;
				break;
			case OPERATOR_PROX:
				operator = decodeSequenceProximityOperator(bAStream,length);
				break;
			default:
				System.out.println("decodeChoiceOperator");
				break;
		}//switch
		
		return operator;
	}//decodeChoiceOperator
	
	
	public ProximityOperator decodeSequenceProximityOperator(ByteArrayInputStream bAStream,int len)
	throws Exception{
		ProximityOperator proximityOperator = new ProximityOperator();
		byte[] BA = AsnUtil.readNBytes(bAStream,len);
		ByteArrayInputStream bsSeq = new ByteArrayInputStream(BA);
		while(ZERO < bsSeq.available()){
			AsnUtil.TagInfo tagInfo = AsnUtil.decodeTag(bsSeq);
			int length = AsnUtil.decodeLength(bsSeq);
			switch(tagInfo.tag){
				case EXCLUSION:
					proximityOperator.setExclusion(BerDecoder.decodeBoolean(bsSeq));
					break;
				case DISTANCE:
					proximityOperator.setDistance(BerDecoder.decodeInteger(bsSeq,length));
					break;
				case ORDERED:
					proximityOperator.setOrdered(BerDecoder.decodeBoolean(bsSeq));
					break;
				case RELATION_TYPE:
					proximityOperator.setRelationType(BerDecoder.decodeInteger(bsSeq,length));
					break;
				case PROXIMITY_UNIT_CODE:
					proximityOperator.setProximityUnitCode(decodeChoiceProximityUnitCode(bsSeq));
					break;
				default:
					System.out.println("decodeSequenceProximityOperator");
					break;
			}//switch
			  
		}//while
		return proximityOperator;
	}//decodeSequenceProximityOperator
	
	
	public ProximityUnitCode decodeChoiceProximityUnitCode(ByteArrayInputStream bAStream)
	throws Exception{
		ProximityUnitCode proximityUnitCode = null;
		AsnUtil.TagInfo tagInfo = AsnUtil.decodeTag(bAStream);
		int length = AsnUtil.decodeLength(bAStream);
		switch(tagInfo.tag){
			case KNOWN_PROXIMITY_UNIT:
				KnownProximityUnit knownProximityUnit = new KnownProximityUnit();
				knownProximityUnit.setKnownProximityUnit(BerDecoder.decodeInteger(bAStream,length));
				proximityUnitCode = knownProximityUnit;
				break;
			case PROXIMITY_PRIVATE:
				ProximityPrivate proximityPrivate = new ProximityPrivate();
				proximityPrivate.setPrivate(BerDecoder.decodeInteger(bAStream,length));
				proximityUnitCode = proximityPrivate;
				break;	
			default:
				System.out.println("decodeChoiceProximityUnitCode");
				break;
		}//switch
		return proximityUnitCode;
	}//decodeChoiceProximityUnitCode
	
	
	public Operand decodeChoiceOperand(ByteArrayInputStream bAStream)
	throws Exception{
		Operand operand = null;
		AsnUtil.TagInfo tagInfo = AsnUtil.decodeTag(bAStream);
		int length = AsnUtil.decodeLength(bAStream);
		switch(tagInfo.tag){
			case ATTRIBUTES_PLUS_TERM:
				operand = decodeSequenceAttributePlusTerm(bAStream,length);
				break;
			case RESULT_SET_ID:
				ResultSetId resultSetId = new ResultSetId();
				resultSetId.setResultSetId(BerDecoder.decodeVisibleString(bAStream,length));
				operand = resultSetId;
				break;
			case RESULT_SET_PLUS_ATTRIBUTES:
				operand = decodeSequenceResultSetPlusAttributes(bAStream,length);
				break;
			default:
				System.out.println("decodeChoiceOperand");
				break;
		}//switch
		return operand;
	}//decodeChoiceOperand
	
	
	public ResultSetPlusAttributes decodeSequenceResultSetPlusAttributes(ByteArrayInputStream bAStream,int len)
	throws Exception{
		ResultSetPlusAttributes resultSetPlusAttributes = new ResultSetPlusAttributes();
		
		AsnUtil.TagInfo tagInfo = AsnUtil.decodeTag(bAStream);
		int length = AsnUtil.decodeLength(bAStream);
		assert tagInfo.tag == RESULT_SET_ID:"decodeSequenceResultSetPlusAttributes-ResultSetId";
		resultSetPlusAttributes.setResultSet(BerDecoder.decodeVisibleString(bAStream,len));
		
		tagInfo = AsnUtil.decodeTag(bAStream);
		length = AsnUtil.decodeLength(bAStream);
		assert tagInfo.tag == ATTRIBUTE_LIST:"decodeSequenceResultSetPlusAttributes-ResultSetId";
		resultSetPlusAttributes.setAttributes(decodeAttributeList(bAStream,len));
		
		return resultSetPlusAttributes;
	}//
	
	
	public AttributesPlusTerm decodeSequenceAttributePlusTerm(ByteArrayInputStream bAStream,int len)
	throws Exception{
		AttributesPlusTerm attributesPlusTerm = new AttributesPlusTerm();
		
		AsnUtil.TagInfo tagInfo = AsnUtil.decodeTag(bAStream);
		int length = AsnUtil.decodeLength(bAStream);
		assert tagInfo.tag == ATTRIBUTE_LIST:"decodeSequenceAttributePlusTerm-AttributeList";
		attributesPlusTerm.setAttributes(decodeAttributeList(bAStream,length));
		
		attributesPlusTerm.setTerm(decodeChoiceTerm(bAStream));
		
		return attributesPlusTerm;
	}//decodeSequenceAttributePlusTerm
	
	
	public Term decodeChoiceTerm(ByteArrayInputStream bAStream)
	throws Exception{
		Term term = null;
		AsnUtil.TagInfo tagInfo = AsnUtil.decodeTag(bAStream);
		int length = AsnUtil.decodeLength(bAStream);
		switch(tagInfo.tag){
			case TERM_GENERAL:
				Term_General termGeneral = new Term_General();
				termGeneral.setGeneral(BerDecoder.decodeOctetString(bAStream,length));
				term = termGeneral;
				break;	
			case TERM_NUMERIC:
				Term_Numeric termNumeric = new Term_Numeric();
				termNumeric.setNumeric(BerDecoder.decodeInteger(bAStream,length));
				term = termNumeric;
				break;
			case TERM_CHARACTER_STRING:
				Term_CharacterString termCharacterString = new Term_CharacterString();
				termCharacterString.setCharacterString(BerDecoder.decodeInteger(bAStream,length));
				term = termCharacterString;
				break;
			case TERM_OID:
				Term_OID termOid = new Term_OID();
				termOid.setOid(BerDecoder.decodeObjectIdentifier(bAStream,length));
				term = termOid;
				break;
			case TERM_DATE_TIME:
				//TODO: AsnGeneralizedTime 
				break;
			case TERM_EXTERNAL:
				//TODO: External
				break;
			case TERM_INTEGER_AND_UNIT:
				Term_IntUnit termIntegerAndUnit = new Term_IntUnit();
				tagInfo = AsnUtil.decodeTag(bAStream);
				length = AsnUtil.decodeLength(bAStream);
				assert tagInfo.tag == INT_UNIT_VALUE:"decodeChoiceTerm-intUnit-Value";
				
				termIntegerAndUnit.setValue(BerDecoder.decodeInteger(bAStream,length));
				
				tagInfo = AsnUtil.decodeTag(bAStream);
				length = AsnUtil.decodeLength(bAStream);
				assert tagInfo.tag == INT_UNIT_UNIT_USED:"decodeChoiceTerm-intUnit-unitUsed";
				termIntegerAndUnit.setUnitUsed(decodeSequenceUnit(bAStream,length));
				
				term = termIntegerAndUnit;
				break;
			case TERM_NULL:
				Term_Null termNull = new Term_Null();
				term = termNull; 
				break;
			default:
				System.out.println("decodeChoiceTerm");
				break;
		}//switch
		return term;
	}//decodeChoiceTerm
	
	
	public Unit decodeSequenceUnit(ByteArrayInputStream bAStream,int len)
	throws Exception{
		int length;
		Unit unit = new Unit();
		byte[] BA = AsnUtil.readNBytes(bAStream,len);
		ByteArrayInputStream bsSeq = new ByteArrayInputStream(BA);
		
		while(ZERO < bsSeq.available()){
			AsnUtil.TagInfo tagInfo = AsnUtil.decodeTag(bsSeq);
			switch(tagInfo.tag){	
				case UNIT_SYSTEM:
					tagInfo = AsnUtil.decodeTag(bsSeq);
					length = AsnUtil.decodeLength(bsSeq);
					unit.setUnitSystem(BerDecoder.decodeVisibleString(bsSeq,length));
					break;
				case UNIT_TYPE:
					length = AsnUtil.decodeLength(bsSeq);
					unit.setUnitType(decodeChoiceStringOrNumeric(bsSeq));
					break;
				case UNIT:
					length = AsnUtil.decodeLength(bsSeq);	
					unit.setUnit(decodeChoiceStringOrNumeric(bsSeq));
					break;
				case SCALE_FACTOR:
					length = AsnUtil.decodeLength(bsSeq);
					unit.setScaleFactor(BerDecoder.decodeInteger(bsSeq,length));
					break;
				default:
					System.out.println("decodeSequenceUnit");
					break;
			}//switch
		}//while
		
		return unit;
	}//decodeSequenceIntUsed
	
	
	public AttributeList decodeAttributeList(ByteArrayInputStream bAStream, int len)
	throws Exception{
		AttributeList attributeList = new AttributeList();
		attributeList.setAttributeElement(decodeSequenceOfAttributeElement(bAStream,len));
		return attributeList;
	}//decodeAttributeList
	
	
	public AttributeElement[] decodeSequenceOfAttributeElement(ByteArrayInputStream bAStream, int len)
	throws Exception{
		List<AttributeElement> attributeElementList = new ArrayList<AttributeElement>();
		byte[] BA = AsnUtil.readNBytes(bAStream,len);
		ByteArrayInputStream bsSeq = new ByteArrayInputStream(BA);
		while(ZERO < bsSeq.available()){
			AsnUtil.TagInfo tagInfo = AsnUtil.decodeTag(bsSeq);
			int length = AsnUtil.decodeLength(bsSeq);
			assert tagInfo.tag == SEQUENCE:"decodeSequenceOfAttributeElement:Sequence";
			attributeElementList.add(decodeSequenceAttributeElement(bsSeq,len));
		}//while
		return (AttributeElement[])(attributeElementList.toArray(new AttributeElement[0]));
	}//decodeSequenceOfAttributeElement
	
	
	public AttributeElement decodeSequenceAttributeElement(ByteArrayInputStream bAStream,int len)
	throws Exception{
		AttributeElement attributeElement = new AttributeElement();
		AsnUtil.TagInfo tagInfo = AsnUtil.decodeTag(bAStream);
		int length = AsnUtil.decodeLength(bAStream);
		
		switch(tagInfo.tag){
			case OBJECT_IDENTIFIER:				
				attributeElement.setAttributeSet(BerDecoder.decodeObjectIdentifier(bAStream,length));
				tagInfo = AsnUtil.decodeTag(bAStream);
				length = AsnUtil.decodeLength(bAStream);
				attributeElement.setAttributeType(BerDecoder.decodeInteger(bAStream,length));
				break;
			case ATTRIBUTE_TYPE:
				attributeElement.setAttributeType(BerDecoder.decodeInteger(bAStream,length));
				break;
			default:
				System.out.println("decodeSequenceAttributeElement");
				break;
		}//switch		
		
		attributeElement.setAttributeValue(decodeChoiceAttributeValue(bAStream));
		return attributeElement;	
	}//decodeSequenceAttributeElement
	
	
	public AttributeValue decodeChoiceAttributeValue(ByteArrayInputStream bAStream)
	throws Exception{
		AttributeValue attributeValue = null;
		AsnUtil.TagInfo tagInfo = AsnUtil.decodeTag(bAStream);
		int length = AsnUtil.decodeLength(bAStream);
		switch(tagInfo.tag){
			case NUMERIC:
				Numeric numeric = new Numeric();
				numeric.setNumeric(BerDecoder.decodeInteger(bAStream,length));
				attributeValue = numeric;
				break;
			case COMPLEX:
				attributeValue = decodeSequenceComplex(bAStream,length);
				break;
			default:
				System.out.println("decodeChoiceAttributeValue");
				break;
		}//switch
		
		return attributeValue;
	}//decodeAttributeValue
	
	
	public Complex decodeSequenceComplex(ByteArrayInputStream bAStream, int len)
	throws Exception{
		Complex complex = new Complex();
		byte[] BA = AsnUtil.readNBytes(bAStream,len);
		ByteArrayInputStream bsSeq = new ByteArrayInputStream(BA);
		while(ZERO < bsSeq.available()){
			AsnUtil.TagInfo tagInfo = AsnUtil.decodeTag(bsSeq);
			int length = AsnUtil.decodeLength(bsSeq);
			switch(tagInfo.tag){
				case LIST:
					complex.setList(decodeSequenceOfStringOrNumeric(bsSeq,length));
					break;
				case SEMANTIC_ACTION:
					complex.setSemanticAction(decodeSequenceOfAsnInteger(bsSeq,length));
					break;
				default:
					System.out.println("decodeSequenceComplex");
					break;
			}//switch
		}//while
					
		return complex;
	}//decodeComplex
	
	
	public AsnInteger[] decodeSequenceOfAsnInteger(ByteArrayInputStream bAStream, int len)
	throws Exception{
		List<AsnInteger> asnIntegerList = new ArrayList<AsnInteger>();
		
		byte[] BA = AsnUtil.readNBytes(bAStream,len);
		ByteArrayInputStream bsSeq = new ByteArrayInputStream(BA);
		while(ZERO < bsSeq.available()){
			AsnUtil.TagInfo tagInfo = AsnUtil.decodeTag(bsSeq);
			int length = AsnUtil.decodeLength(bsSeq);
			assert tagInfo.tag == INTEGER:"decodeSequenceOfAsnInteger-Integer";
			asnIntegerList.add(BerDecoder.decodeInteger(bsSeq,length));
		}//while
		return (AsnInteger[])(asnIntegerList.toArray(new AsnInteger[0]));
	}//decodeSequenceOfAsnInteger
	
	
	public StringOrNumeric[] decodeSequenceOfStringOrNumeric(ByteArrayInputStream bAStream,int len)
	throws Exception{
		List<StringOrNumeric> stringOrNumeric = new ArrayList<StringOrNumeric>();
		byte[] stringOrNumericBA = AsnUtil.readNBytes(bAStream,len);
		ByteArrayInputStream bs = new ByteArrayInputStream(stringOrNumericBA);
		while(ZERO < bs.available()){
			stringOrNumeric.add(decodeChoiceStringOrNumeric(bs));
		}//while
		return (StringOrNumeric[])(stringOrNumeric.toArray(new StringOrNumeric[0]));
	}//decodeSequenceOfStringOrNumeric
	
	
	
	public StringOrNumeric decodeChoiceStringOrNumeric(ByteArrayInputStream bAStream)
	throws Exception{
		StringOrNumeric stringOrNumeric = null;
		AsnUtil.TagInfo tagInfo = AsnUtil.decodeTag(bAStream);
		int length = AsnUtil.decodeLength(bAStream);
		switch(tagInfo.tag){
			case STRING_OR_NUMERIC_STRING:
				StringOrNumeric_String stringOrNumeric_String = new StringOrNumeric_String();
				stringOrNumeric_String.setString(BerDecoder.decodeVisibleString(bAStream,length));
				stringOrNumeric = stringOrNumeric_String;				
				break;
			case STRING_OR_NUMERIC_NUMERIC:
				StringOrNumeric_Numeric stringOrNumeric_Numeric = new StringOrNumeric_Numeric();
				stringOrNumeric_Numeric.setNumeric(BerDecoder.decodeInteger(bAStream,length));
				stringOrNumeric = stringOrNumeric_Numeric;
				break;
			default:
				System.out.println("decodeChoiceStringOrNumeric");
				break;
		}//switch
		return stringOrNumeric;
	}//decodeChoiceStringOrNumeric
	
	
	
	
	public ElementSetNames decodeChoiceElementSetNames(ByteArrayInputStream bAStream)
	throws Exception{
		AsnUtil.TagInfo tagInfo = AsnUtil.decodeTag(bAStream);
		int length = AsnUtil.decodeLength(bAStream);
		ElementSetNames elementSetNames = null;
		switch(tagInfo.tag){
			case GENERIC_ELEMENT_SET_NAME:
				GenericElementSetName genericElementSetName = new GenericElementSetName();
				genericElementSetName.setGenericElementSetName(BerDecoder.decodeVisibleString(bAStream,length));
				elementSetNames = genericElementSetName;
				break;
			case DATABASE_SPECIFIC:
				DatabaseSpecific dbSpecific = decodeSequenceOfDatabaseSpecific(bAStream,length);
				elementSetNames = dbSpecific;
				break;
			default:
				System.out.println("decodeChoiceElementNames");
				break;
		}//switch
		return elementSetNames;
	}//decodeChoiceElementSetNames
	
	
	public DatabaseSpecific decodeSequenceOfDatabaseSpecific(ByteArrayInputStream bAStream,int len)
	throws Exception{
		List<DatabaseElementPair> dbSpecific = new ArrayList<DatabaseElementPair>();
		byte[] databaseSpecificBA;
		databaseSpecificBA = AsnUtil.readNBytes(bAStream,len);
		ByteArrayInputStream bsSeq = new ByteArrayInputStream(databaseSpecificBA);
		while(ZERO != bsSeq.available()){
			DatabaseElementPair dbElementPair = new DatabaseElementPair();
			
			//decode sequence [tag/length]
			AsnUtil.TagInfo tagInfo = AsnUtil.decodeTag(bsSeq);
			int length = AsnUtil.decodeLength(bsSeq);
			assert tagInfo.tag == SEQUENCE:"decodeSequenceOfDatabaseSpecific-InvalidTag-Sequence"; 
			
			tagInfo = AsnUtil.decodeTag(bsSeq);
			length = AsnUtil.decodeLength(bsSeq);
			assert tagInfo.tag == DATABASE_NAME:"decodeSequenceOfDatabaseSpecific-InvalidTag-DatabaseName";
			dbElementPair.setDbName(BerDecoder.decodeVisibleString(bsSeq,length));
			
			tagInfo = AsnUtil.decodeTag(bsSeq);
			length = AsnUtil.decodeLength(bsSeq);
			assert tagInfo.tag == ELEMENT_SET_NAME:"decodeSequenceOfDatabaseSpecific-InvalidTag-ElementSetName";
			dbElementPair.setEsn(BerDecoder.decodeVisibleString(bsSeq,length));
			
			dbSpecific.add(dbElementPair); 
		}//while
		DatabaseSpecific databaseSpecific = new DatabaseSpecific();
		databaseSpecific.setDatabaseElementPair(dbSpecific.toArray(new DatabaseElementPair[0]));
		 
		return databaseSpecific;		
	}//decodeSequenceOfDatabaseSpecific
	
	
	public AsnVisibleString[] decodeSequenceOfDatabase(ByteArrayInputStream bAStream,int len)
	throws Exception{
		List<AsnVisibleString> dbNamesList = new ArrayList<AsnVisibleString>();
		byte[] databaseNames;
		databaseNames = AsnUtil.readNBytes(bAStream,len);
		ByteArrayInputStream bsSeq = new ByteArrayInputStream(databaseNames);
		while(ZERO != bsSeq.available()){
			AsnUtil.TagInfo tagInfo = AsnUtil.decodeTag(bsSeq);
			int length = AsnUtil.decodeLength(bsSeq);
			
			assert tagInfo.tag == DATABASE_NAME:"decodeSequenceOfDatabase-InvalidTag";			
			
			dbNamesList.add(BerDecoder.decodeVisibleString(bsSeq,length));		
		}//while
		
		return (AsnVisibleString[])(dbNamesList.toArray(new AsnVisibleString[0]));
		
	}//decodeSequenceOfDatabase
	
}
